// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#include <stdarg.h>
#include <time.h>

#ifdef NETWORK
#include "network.h"
#include "post.h"
#include "interrupt.h"
#endif

#ifdef CHANGED
#include "../threads/synch.h"

struct KernelLock{
	Lock* lock; /* the lock to be stored */
	AddrSpace* addrSpace; /* the address space of the owning thread */
	int inUse; /* how many processes are trying to acquire the lock */
	int toBeDeleted;
	
	KernelLock(Lock* l, AddrSpace* a){
		lock = l;
		addrSpace = a;
		inUse = 0;
		toBeDeleted = 0;
	}
};

struct KernelCondition{
	Condition* condition; /* the condition to be stored */
	AddrSpace* addrSpace; /* the address space of the owning thread */
	int inUse; /* how many processes are waiting for the condition */
	int toBeDeleted;
	
	KernelCondition(Condition* c, AddrSpace* a){
		condition = c;
		addrSpace = a;
		inUse = 0;
		toBeDeleted = 0;
	}
};

const int MAX_LOCKS = 500;
const int MAX_CONDITIONS = 500;
#ifdef NETWORK
const int MAX_MONITORS = 500;
#endif

Lock* kernelLockLock = new Lock("KernelLock Lock");
KernelLock* kernelLocks[MAX_LOCKS]; 

Lock* kernelConditionLock = new Lock("KernelCondition Lock");
KernelCondition* kernelConditions[MAX_CONDITIONS]; 

#ifdef NETWORK
int nextMonitorIndex = 0;
#endif
#ifndef NETWORK
int nextLockIndex = 0;
int nextConditionIndex = 0;
#endif
int currentTLB = 0;

#endif

#ifdef NETWORK
PacketHeader outPktHdr;
PacketHeader inPktHdr;
MailHeader outMailHdr;
MailHeader inMailHdr;
char receivedMessage[MaxMailSize];
//bool success;
#endif

using namespace std;

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

//Function for swaping pages when physical memory is full
int HandlePageEviction(int badvpn){

	int evicted = -1;

	#ifdef USE_TLB
	
	iptLock->Acquire();
	
	//Reads if the random flag was set, otherwise uses the FIFO policy
	//Decides which page will try to be evicted
	do{
	
		if(PRAND){			
				evicted = rand() % NumPhysPages;			
		}
		else{
		
			//if evicted had been set in the previous loop (it didn't belong to that process push onto the queue
			if(evicted != -1){
				fifoPages->Append((void*)evicted);	
			}
		
			evicted = (int)(fifoPages->Remove());
			
		}
	}while(ipt[evicted].use == true || ipt[evicted].spaceID != currentThread->spaceId);
	/*Professor Crowley said we're allowed to implement a policy that only swaps pages local to the process*/
	
	
	ipt[evicted].use = true;
	iptLock->Release();
		
	DEBUG('b',"Chose a page to kick out %i\n", evicted);
		
	//invalidated page about to be evicted and propogating the dirty bit
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	for (int i = 0; i < TLBSize; i++) {
	
	  // if (machine->tlb[i].virtualPage == ipt[evicted].virtualPage && processTable[ipt[evicted].spaceID]->parentAddrSpace->spaceID == ipt[evicted].spaceID && machine->tlb[i].valid == true){
		if (machine->tlb[i].physicalPage == evicted && machine->tlb[i].valid == true){
			//printf("-----------currentThread->spaceId: %i, addressspace spaceID %i, ipt evicted spaceiD %i\n", currentThread->spaceId,  processTable[ipt[evicted].spaceID]->parentAddrSpace->spaceID, ipt[evicted].spaceID);

			machine->tlb[i].valid = false;	
			
			if(machine->tlb[i].dirty == true){
				ipt[evicted].dirty = true;
			}				
		}	  
	}
    (void) interrupt->SetLevel(oldLevel);
	
	//If the evicted page is dirty, we must save it in the swap file
	if(ipt[evicted].dirty == true){
	
		int swapLoc;
		
		//if the page has never been written to the swap file before, assign it a location in the swap file
		if(processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].pageLocation != PageInSwapFile){
		
			swapLock->Acquire();
			swapLoc = swapUsedPages;
			swapUsedPages++;

			//swap file initialized to 51200 * PageSize in system.cc
			if(swapLoc > 51200){
				printf("Error: the swap file size must be increased\n");
			};
			
			iptLock->Acquire();
			swapLock->Release();
			//update the page table with the swap location, is new page location
			processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Acquire();			
			processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].pageLocation = PageInSwapFile;
			processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].swapLocation = swapLoc;
			processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Release();
			iptLock->Release();
			
		}
		
		//printf("Writing to swap file - evicted: %i, byteOffset: %i , swapLoc %i\n", evicted,processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].swapLocation * PageSize, processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].swapLocation);
		//Writing to the swap file (overwriting if it's been there before)
		swapLock->Acquire();
		swapfile->WriteAt(&(machine->mainMemory[evicted * PageSize]), PageSize, processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].swapLocation * PageSize);
		swapLock->Release();
		//printf("End of writing\n");
		
		//now invalidate the physical page, and set dirty to false so that it won't be re-written again until necessary
		processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Acquire();
		processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].physicalPage = -1;
		processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].valid = true;
		processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].dirty = false;
		processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Release();
		
		DEBUG('h',"after updating pagetable\n");
	}
	
	
	DEBUG('h',"before pagetable\n");
	processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Acquire();
	
	//printf("ipt entries: PPN: %i, VPN: %i, dirty: %i, use: %i, readOnly %i, spaceID: %i, timestamp: %i, valid: %i\n", ipt[evicted].physicalPage, ipt[evicted].virtualPage,ipt[evicted].dirty, ipt[evicted].use, ipt[evicted].readOnly, ipt[evicted].spaceID,ipt[evicted].timestamp, ipt[evicted].valid);	

	if(processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable == NULL){
	  printf("PageTable is null for spaceID %i\n", ipt[evicted].spaceID);
	}

	
	//invalidating the physical page (all that needs to be done if not dirty, and redundant if it was dirty
	processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTable[ipt[evicted].virtualPage].physicalPage = -1;
	processTable[ipt[evicted].spaceID]->parentAddrSpace->pageTableLock->Release();
	DEBUG('h',"after pagetable\n");
	

	DEBUG('e', "Eviction - VPN: %i, PPN: %i, Offset: %i, Loc: %i\n", badvpn, evicted, evicted*PageSize, processTable[ipt[evicted].spaceID]->parentAddrSpace->spaceID);

	
	#endif
	
	
	
	return evicted;

}

//function for handling situations where the page hasn't been populated to the IPT yet
int HandleIPTMiss(int badvpn){
	int physPage = -1;

	#ifdef USE_TLB

	//populating the itp for step 3, bitmap and population take place between locks
	
	bitMapLock->Acquire();
	physPage = iptUsedPages->Find();
	bitMapLock->Release();
	
	//if there isn't any room left, evict a page
	if(physPage < 0){
		physPage = HandlePageEviction(badvpn);
	}
		
	currentThread->space->pageTableLock->Acquire();
	currentThread->space->pageTable[badvpn].physicalPage = physPage;
	
	DEBUG('e', "IPT Load - VPN: %i, PPN: %i, Offset: %i, Loc: %i\n", badvpn, physPage, currentThread->space->pageTable[badvpn].byteOffset, currentThread->space->pageTable[badvpn].pageLocation);
	
	//If the current page is in the executable, read it from there
	if(currentThread->space->pageTable[badvpn].pageLocation == PageInExecutable){
		DEBUG('b',"Reading from Exe file at physPage: %i, vp: %i with offset %i\n", physPage, badvpn, currentThread->space->pageTable[badvpn].byteOffset);
		currentThread->space->exeptr->ReadAt(&(machine->mainMemory[physPage * PageSize]), PageSize, currentThread->space->pageTable[badvpn].byteOffset);
	} 
	else if(currentThread->space->pageTable[badvpn].pageLocation == PageInSwapFile){
		//the page is in the swap file, so it is being read from there
		DEBUG('b',"Reading from Swap file at swap location: %i, with offset %i\n", currentThread->space->pageTable[badvpn].swapLocation, currentThread->space->pageTable[badvpn].swapLocation*PageSize);
		swapfile->ReadAt(&(machine->mainMemory[physPage * PageSize]), PageSize, currentThread->space->pageTable[badvpn].swapLocation*PageSize);
		DEBUG('b',"Finished Read from Swap file at swap location: %i, with offset %i\n", currentThread->space->pageTable[badvpn].swapLocation, currentThread->space->pageTable[badvpn].swapLocation*PageSize);

	}
	//otherwise don't touch the data

	currentThread->space->pageTableLock->Release();
	
	//update the ipt
	//iptLock->Acquire();
	ipt[physPage].virtualPage = badvpn;
	ipt[physPage].physicalPage = physPage;
	ipt[physPage].valid = TRUE;
	ipt[physPage].use = FALSE;
	ipt[physPage].dirty = FALSE;
	ipt[physPage].readOnly = FALSE;
	ipt[physPage].spaceID = currentThread->space->spaceID;
	//iptLock->Release();
	
	//update the pagetable that it's not in use (may have been in use in the HandlePageEviction
	currentThread->space->pageTableLock->Acquire();
	currentThread->space->pageTable[badvpn].use = false;
	fifoPages->Append((void*)physPage);
	currentThread->space->pageTableLock->Release();
	#endif
	
	return physPage;
}

/* Page Fault Exception - assigning physical pages to things that need to be executed*/
void HandlePageFault(){
		
	/* A. Get the virtual page required by dividing the virtual address by the page size. */
	int badVAddr = machine->ReadRegister(BadVAddrReg); //register 39
	int badVirtualPage = badVAddr / PageSize;
	
	DEBUG('d', "badVirtualPage = %i\n",badVirtualPage);
	
	/* B. Find that entry in the current thread's page table. 
	   C. Copy the virtual and physical page numbers from the page table to the slot in the TLB. */
	AddrSpace* space = currentThread->space;
	
	//printf("badvpn: %i, numpages: %i\n", badVirtualPage, space->getNumPages());
	
	if(badVirtualPage >= space->getNumPages()){
		printf("ERROR: VPN does not exist for the given address space! badVirtualPage: %i\n", badVirtualPage);
		//return;
	}
	
	
	int physicalPage = -1;
	
	#ifdef USE_TLB
	

	for(int i = 0; i < NumPhysPages; i++){
		//find the vpn's ppn, make sure it's valid and the correct address space
		if(ipt[i].virtualPage == badVirtualPage && ipt[i].valid == TRUE && space->spaceID == ipt[i].spaceID){
			physicalPage = ipt[i].physicalPage;
			break;
		}
	}
	
	//if the virtual page doesn't have a physical page, generate an IPT miss to give it a ppn
	if(physicalPage == -1){
	
		physicalPage = HandleIPTMiss(badVirtualPage);
	
	}
	
	//Update the TLB with the new information
	IntStatus oldLevel = interrupt->SetLevel(IntOff); //turn off interrupts
	
	fifoPages->Append((void*)physicalPage);
	
	currentTLB = (currentTLB + 1) % TLBSize; //change where TLBSize is?
	
	if(machine->tlb[currentTLB].valid == true && machine->tlb[currentTLB].dirty == true){
		machine->tlb[currentTLB].valid = false;
		int temp = machine->tlb[currentTLB].physicalPage;
		ipt[temp].dirty = machine->tlb[currentTLB].dirty;
	}
	
	machine->tlb[currentTLB].virtualPage = ipt[physicalPage].virtualPage;
	machine->tlb[currentTLB].physicalPage = ipt[physicalPage].physicalPage;
	//machine->tlb[currentTLB].valid = ipt[physicalPage].valid;
	machine->tlb[currentTLB].valid = ipt[physicalPage].valid;
	machine->tlb[currentTLB].readOnly = ipt[physicalPage].readOnly;
	machine->tlb[currentTLB].use = ipt[physicalPage].use;
	machine->tlb[currentTLB].dirty = ipt[physicalPage].dirty;
	//physicalPage = ipt[physicalPage].physicalPage;
	
	DEBUG('f', "VP: %i, PP: %i, Valid: %i, Use: %i, Dirty: %i\n",machine->tlb[currentTLB].virtualPage, machine->tlb[currentTLB].physicalPage, machine->tlb[currentTLB].valid, machine->tlb[currentTLB].use, machine->tlb[currentTLB].dirty);
	
	(void) interrupt->SetLevel(oldLevel); //restore interrupts
	
	#endif
	
	//increment currentTLB
	
	
	
}

/* Thread */
void Yield_Syscall(){
	/* just call currentThread->Yield() */
	currentThread->Yield();
}

void kernel_thread(int vaddr){
	sysLock->Acquire();
	//printf("KERNEL THREAD %i\n", vaddr);
	/* write to the register PCReg the virtual address. */
	
	//printf("machine->WriteRegister(PCReg, vaddr)\n");
	machine->WriteRegister(PCReg, vaddr);
	
	/* write virtualaddress + 4 in NextPCReg. */
	//printf("machine->WriteRegister(NextPCReg, vaddr+4)\n");
	machine->WriteRegister(NextPCReg, vaddr+4);

	/* call RestoreState function in order to prevent information loss while context switching. */
	//printf("currentThread->space->RestoreState()\n");
	currentThread->space->RestoreState();
	
	/* write to the stack register, the starting postion of the stack for this thread. */
	//printf("machine->WriteRegister(StackReg, currentThread->startOfStack)\n");
	machine->WriteRegister(StackReg, currentThread->startOfStack); /** virtual address */
	sysLock->Release();
	/* call machine->Run() */
	//printf("machine->Run()\n");
	machine->Run();
}

void Fork_Syscall(int func){
	//printf("FORK SYSCALL %i\n", func);
	sysLock->Acquire();
	int error = 0; /* error code */
	if(func < 0 || func > PageSize*NumPhysPages){ /* check if function is valid */
		error = 1;
		printf("ERROR: invalid function in Fork Syscall!\n");
	}
	
	processTableLock->Acquire();
	//printf("numThreads = %i\n", processTable[currentThread->spaceId]->numThreads);
	#ifdef NETWORK
	if(processTable[currentThread->spaceId]->getNumThreads() >= MAX_THREADS){
	#else
	if(processTable[currentThread->spaceId]->numThreads >= MAX_THREADS){
	#endif
		error = 1;
		printf("ERROR: MAX_THREADS already reached!\n");
	}
	processTableLock->Release();
	
	if(error != 1){
		/* create a new thread */
		//printf("MAKING A THREAD\n");
		Thread* t = new Thread("Forked Thread");
		
		/* allocate the address space to the thread being forked */
		//printf("t->space = currentThread->space\n");
		t->space = currentThread->space; /* set new threads address space to current thread's address space because threads share the process address space */
		//printf("MakeStackPages()!\n");
		t->startOfStack = t->space->MakeStackPages(); /* allocate stack space for the new thread and store the start of the stack in the thread */
		//printf("t->spaceId = currentThread->spaceId\n");
		t->spaceId = currentThread->spaceId; /* set the spaceId to the current thread's spaceId */
		t->space->spaceID = currentThread->spaceId;
		//printf("startOfStack = %i; PageSize = %i\n", t->startOfStack, PageSize);
		
		/* update process table for multi programming */
		//printf("processTable[t->spaceId]->processTableEntryLock->Acquire()\n");
		processTable[t->spaceId]->processTableEntryLock->Acquire();
		//printf("processTable[t->spaceId]->numThreads++\n");
		#ifdef NETWORK
		processTable[t->spaceId]->incrementNumThreads();
		//printf("fork: current thread spaceId = %i, threadId = %i, numThreads = %i\n", currentThread->spaceId, currentThread->getThreadId(), processTable[t->spaceId]->getNumThreads());
		t->setThreadId(processTable[t->spaceId]->getNumThreads());
		//printf("fork: current thread spaceId = %i, threadId = %i, numThreads = %i\n", t->spaceId, t->getThreadId(), processTable[t->spaceId]->getNumThreads());
		#else
		processTable[t->spaceId]->numThreads++;
		t->setThreadId(processTable[t->spaceId]->numThreads);
		//printf("fork: current thread spaceId = %i, threadId = %i, numThreads = %i\n", t->spaceId, t->getThreadId(), processTable[t->spaceId]->numThreads);
		#endif
		//printf("processTable[t->spaceId]->numThreads = %i\n", processTable[t->spaceId]->numThreads);
		//printf("processTable[t->spaceId]->numAliveThreads++\n");
		processTable[t->spaceId]->numAliveThreads++;
		//printf("processTable[t->spaceId]->processTableEntryLock->Release()\n");
		processTable[t->spaceId]->processTableEntryLock->Release();
		
		/* fork the kernel thread */
		//printf("t->Fork(kernel_thread, func)\n");
		t->Fork(kernel_thread, func); 
	}
	sysLock->Release();
}

void exec_thread(int vaddr){
	sysLock->Acquire();
	/* Initialize the register by using currentThread->space. */
	//printf("currentThread->space->InitRegisters();\n");
	currentThread->space->InitRegisters();
	
	/* Call Restore State through currentThread->space. */
	//printf("currentThread->space->RestoreState();\n");
	currentThread->space->RestoreState();
	
	/* Call machine->Run() */
	//printf("machine->Run();\n");
	sysLock->Release();
	machine->Run();
	ASSERT(FALSE);
}

SpaceId Exec_Syscall(int name, int length){ /* really just returns an int that is the index of the process */
	
	//printf("EXEC_SYSCALL %i, %i\n", name, length);
	sysLock->Acquire();
	if(numProcesses < MAX_PROCESSES && nextProcessIndex < MAX_PROCESSES){
		/* Read the virtual address of the name of the process from the register R4 virtualAddress = machine->ReadRegister(4). */
		/* ALREADY DONE THROUGH SWITCH STATEMENT */
		
		/* Convert it to the physical address and read the contents from there , which will give you the name of the process to be executed. */
		char* fileName = new char[length+1];
		//printf("int result = copyin(name, length, fileName);\n");
		int result = copyin(name, length, fileName);
		
		if(result == -1){ /* translation failed */
			delete [] fileName; /* garbage collection */
			sysLock->Release();
			return -1;
		} else { /* translation succeeded */
			fileName[length] = '\0'; /* null terminate the file name */
			
			/* Now Open that file using filesystem->Open. Store its openfile pointer. */
			//printf("OpenFile* openedFile = fileSystem->Open(fileName); %s\n", fileName);
			OpenFile* openedFile = fileSystem->Open(fileName);
			delete [] fileName; /* garbage collection */
			
			/* Create new addresspace for this executable file. */
			//printf("AddrSpace* generatedAddrSpace = new AddrSpace(openedFile);\n");
			AddrSpace* generatedAddrSpace = new AddrSpace(openedFile);
			
			#ifndef USE_TLB
		       
			//delete openedFile; 
			/* garbage collection */
			#endif
			
			/* Create a new thread. */
			Thread* t = new Thread("New Process Thread");
			
			/* Allocate the space created to this thread's space. */
			t->space = generatedAddrSpace;
			//printf("t->startOfStack = t->space->MakeStackPages();\n");
			//t->startOfStack = t->space->MakeStackPages();
			//printf("t->space->numPages = %i; generatedAddrSpace->numPages = %i\n", t->space->getNumPages(), generatedAddrSpace->getNumPages());
			t->startOfStack = (t->space->getNumPages())*PageSize-16;
			//printf("t->startOfStack = %i\n", t->startOfStack);
			t->spaceId = nextProcessIndex;
			t->space->pageTableLock->Acquire();
			t->space->spaceID = nextProcessIndex;
			t->space->pageTableLock->Release();
			/* Update the process table and related data structures. */
			//ProcessTableEntry* newProcessTableEntry = new ProcessTableEntry(generatedAddrSpace);
			processTableLock->Acquire();
			ProcessTableEntry* newProcessTableEntry = new ProcessTableEntry(t->space);
			processTable[nextProcessIndex] = newProcessTableEntry;
			
			#ifdef NETWORK
			t->setThreadId(processTable[nextProcessIndex]->getNumThreads());
			//processTable[nextProcessIndex]->incrementNumThreads();
			#else
			t->setThreadId(processTable[nextProcessIndex]->numThreads);
			//processTable[nextProcessIndex]->numThreads++;
			#endif
			
			//printf("exec: thread spaceId = %i, threadId = %i\n", t->spaceId, t->getThreadId());
			
			int spaceId = nextProcessIndex;
			numProcesses++;
			nextProcessIndex++;
			processTableLock->Release();
			
			/* Write the space ID to the register 2. */
			//printf("machine->WriteRegister(2, spaceId); spaceId = %i\n", spaceId);
			//machine->WriteRegister(2, spaceId); /* register 2 is where the userprogram looks for the return value */
			
			/* Fork the new thread. I call it exec_thread. */
			//printf("t->Fork(exec_thread, 0);\n");
			t->Fork(exec_thread, 0);
			
			//printf("return spaceId = %i\n", spaceId);
			sysLock->Release();
			return spaceId;
		}
	} else {
		printf("ERROR: Too many processes in Exec Syscall!\n");
		sysLock->Release();
		return -1;
	}
}

void Exit_Syscall(int status){ 

	//printf("-------------------------Anywhere in exit %i\n", status);
	
	/*currentThread->Finish();*/

	/* 3 cases in exit syscall */
	/* 1. last executing thread in the last process. kill nachos by calling interrupt->Halt() 
	   2. not the last executing thread in a process. release the 8 pages of memory allocated for this thread's stack. 
	   3. last executing thread in a process, but not the last process. release all valid memory. release all locks/cvs for this process */
	//printf("EXIT_SYSCALL %i\n", currentThread->spaceId);
	
	sysLock->Acquire();
	printf("exiting with status %i\n", status);
	
	processTable[currentThread->spaceId]->processTableEntryLock->Acquire();
	if(numProcesses == 1 && processTable[currentThread->spaceId]->numAliveThreads == 1){ 
		/* 1. last executing thread in the last process. kill nachos by calling interrupt->Halt() */
		printf("last executing thread in last process\n");
		numProcesses--;
		
		processTable[currentThread->spaceId]->numAliveThreads = 0; /* set the number of alive threads to 0 */
		delete processTable[currentThread->spaceId]->parentAddrSpace; /* garbage collection, could do currentThread->space? */
		
		processTable[currentThread->spaceId]->processTableEntryLock->Release();

		/* kill nachos by calling interrupt->Halt(); */
		sysLock->Release();
		interrupt->Halt();
	} else if(numProcesses > 1 && processTable[currentThread->spaceId]->numAliveThreads == 1){ 
		/* 3. last executing thread in a process, but not the last process. release all valid memory. release all locks/cvs for this process */
		printf("last executing thread in process, not last process\n");
		numProcesses--;
		
		processTable[currentThread->spaceId]->numAliveThreads = 0; /* set the number of alive threads to 0 */
		delete processTable[currentThread->spaceId]->parentAddrSpace; /* garbage collection, could do currentThread->space? */

		processTable[currentThread->spaceId]->processTableEntryLock->Release();

		sysLock->Release();
		
		
		#ifdef USE_TLB
		
		int physPage = -1;
		   
		//for(unsigned int i = 0; i < currentThread->space->numPages; i++){
		  for(unsigned int i = 0; i < currentThread->space->getNumPages(); i++){
		
			for(unsigned int j = 0; j < NumPhysPages; j++){
		
				if(ipt[j].virtualPage == i && ipt[j].valid == TRUE && currentThread->space->spaceID == ipt[j].spaceID){
	
					//clearing the ipt bitmap and population take place between locks
				
					//printf("About to bitmap clear %i\n", ipt[j].physicalPage);
				
					bitMapLock->Acquire();						
					iptUsedPages->Clear(ipt[j].physicalPage);
					bitMapLock->Release();
					
					iptLock->Acquire();
					ipt[j].valid = FALSE;	
				
					iptLock->Release();

					//TODO: don't mess with the tlb....might be messing it up
					
					//invalidating the TLB for this particular entry
					//IntStatus oldLevel = interrupt->SetLevel(IntOff);
					
					//currentTLB = (currentTLB + 1) % TLBSize; 

					//machine->tlb[currentTLB].valid = ipt[j].valid;
					
					//updating the IPT before the TLB is cleared
					//if(machine->tlb[currentTLB].dirty == true){
					//	ipt[machine->tlb[currentTLB].physicalPage].dirty = true;
					//}
					
					//(void) interrupt->SetLevel(oldLevel); 
				}
			
			}
		
		}
		

		#endif
		
		
		
		
		currentThread->Finish();
	} else if(processTable[currentThread->spaceId]->numAliveThreads > 0){ 
		/* 2. not the last executing thread in a process. release the 8 pages of memory allocated for this thread's stack. */
		printf("not last thread in a process\n");
		/* should not decrement numProcesses because not last thread in process */
		
		processTable[currentThread->spaceId]->numAliveThreads--;
		
		#ifdef NETWORK
		processTable[currentThread->spaceId]->decrementNumThreads();
		#else
		processTable[currentThread->spaceId]->numThreads--;
		#endif
				
		processTable[currentThread->spaceId]->parentAddrSpace->ClearStackPages(currentThread->startOfStack); /** is this the appropriate value to be passing in? startOfStack!!! */

		processTable[currentThread->spaceId]->processTableEntryLock->Release();
		sysLock->Release();
	} 
	//printf("before currentThread->Finish();\n");
	currentThread->Finish();
	
	//printf("end exit syscall!\n");
}

/* Lock */
int CreateLock_Syscall(int vaddr, int length){
	
	#ifdef NETWORK
	char *lockName;
	if ( !(lockName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[length] = '\0';
	
		if ( copyin(vaddr,length,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	
	//printf("Creating Lock %s\n", lockName);
	
	
	//char* lockName = new char[20];
	//sprintf(lockName, "m%is%ilock%i", currentThread->getMachineId(), currentThread->spaceId, nextLockIndex);
	//printf("lockName = %s\n", lockName);
	
	int bogusVal = 0;
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s",SC_CreateLock, lockName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 0;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in create lock syscall\n");
		interrupt->Halt();
	}
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	int lockId = atoi(receivedMessage); //parse message
	return lockId;
	#else
	kernelLockLock->Acquire();
	int createdLockIndex = nextLockIndex;
	if(createdLockIndex < MAX_LOCKS - 1){ /* if the lock index is within the bounds of the array */
		char* lockName = new char[20];
		sprintf(lockName, "Lock %i", createdLockIndex);
		
		KernelLock* createdKernelLock = new KernelLock(new Lock(lockName), currentThread->space);
		
		kernelLocks[createdLockIndex] = createdKernelLock;
		//printf("created Lock at index %i\n", createdLockIndex);
		nextLockIndex++; /* increment the lock index for the next lock */
		kernelLockLock->Release();
		return createdLockIndex; /* return the index of the created lock, otherwise return -1 if there was an error */
	} else { /* it's not within the bounds of the array */
		printf("ERROR: Array Index Out of Bounds for Create Lock Syscall!\n");
		createdLockIndex = -1;
		kernelLockLock->Release();
		return createdLockIndex; /* return the index of the created lock, otherwise return -1 if there was an error */
	}
	#endif
}

int DestroyLock_Syscall(int vaddr, int length){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of lock; 3 = lock does not exist */
	
	int errorCode = 0;
	#ifdef NETWORK
	char* lockName;
	if ( !(lockName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[length] = '\0';
	
		if ( copyin(vaddr,length,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	int bogusVal = 0;
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s",SC_DestroyLock, lockName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 0;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in destroy lock syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int lockIndex = vaddr; //TEMP TO FIX COMPILE, BUT SHOULD TECHNICALLY WORK
	
	kernelLockLock->Acquire();
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for index %i for Destroy Lock Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex] == NULL){
		printf("ERROR: Lock at index %i does not exist for Destroy Lock Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Destroy Lock Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		return errorCode;
	}
	
	kernelLocks[lockIndex]->toBeDeleted = 1;
	//printf("Lock at index %i soon to be destroyed!\n", lockIndex);
	kernelLockLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelLocks[lockIndex] != NULL){
			if(kernelLocks[lockIndex]->addrSpace == currentThread->space){
				kernelLocks[lockIndex]->toBeDeleted = 1;
				//printf("Lock at index %i soon to be destroyed!\n", lockIndex);
				kernelLockLock->Release();
				return errorCode;
			} else {
				printf("ERROR: Lock at index %i not owned by process for Destroy Lock Syscall!\n", lockIndex);
				errorCode = 2;
				kernelLockLock->Release();
				return errorCode;
			}
		} else {
			printf("ERROR: Lock at index %i does not exist for Destroy Lock Syscall!\n", lockIndex);
			errorCode = 3;
			kernelLockLock->Release();
			return errorCode;
		}
	}*/
}

int AcquireLock_Syscall(int vaddr, int length){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of lock; 3 = lock does not exist */
	
	int errorCode = 0;
	#ifdef NETWORK
	char* lockName;
	
	
	if ( !(lockName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
		
		lockName[length] = '\0';
	
		if ( copyin(vaddr,length,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	int bogusVal = 0;
	char msg[MaxMailSize];

	sprintf(msg,"%i %s",SC_AcquireLock, lockName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 0;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	//printf("spaceId = %i\n", currentThread->spaceId);
	outMailHdr.length = strlen(msg) + 1;
	//printf("about to send\n");
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	//printf("sent\n");
	if(!success){
		printf("postOffice send failed in acquire lock syscall\n");
		interrupt->Halt();
	}
	//printf("about to receive\n");
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	//printf("received\n");
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int lockIndex = vaddr; //TEMP TO FIX COMPILE, BUT SHOULD TECHNICALLY WORK
	//printf("about to acquire kernelLockLock in acquire lock %i syscall!\n", lockIndex);
	kernelLockLock->Acquire();
	//printf("acquired kernelLockLock in acquire lock %i syscall!\n", lockIndex);
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for index %i for Acquire Lock Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		return errorCode;
	} 
	
	if(kernelLocks[lockIndex] == NULL || kernelLocks[lockIndex]->lock == NULL){
		printf("ERROR: Lock at index %i does not exist for Acquire Lock Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Acquire Lock Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		return errorCode;
	}
	
	kernelLocks[lockIndex]->inUse++;
	//printf("Lock at index %i successfully trying to acquire! inUse = %i\n", lockIndex, kernelLocks[lockIndex]->inUse);
	kernelLockLock->Release();
	kernelLocks[lockIndex]->lock->Acquire();
	kernelLocks[lockIndex]->inUse--;

	//printf("Lock at index %i successfully acquired! inUse = %i\n", lockIndex, kernelLocks[lockIndex]->inUse);
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelLocks[lockIndex] != NULL && kernelLocks[lockIndex]->lock != NULL){ // updated the if to check if the kernelLock's lock is null
			if(kernelLocks[lockIndex]->addrSpace == currentThread->space){
				printf("Lock at index %i successfully trying to acquire!\n", lockIndex);
				kernelLockLock->Release();
				kernelLocks[lockIndex]->lock->Acquire();
				kernelLocks[lockIndex]->inUse++;
				printf("Lock at index %i successfully acquired!\n", lockIndex);
			} else {
				printf("ERROR: Lock at index %i not owned by process for Acquire Lock Syscall!\n", lockIndex);
				errorCode = 2;
			}
		} else {
			printf("ERROR: Lock at index %i does not exist for Acquire Lock Syscall!\n", lockIndex);
			errorCode = 3;
		}
	}*/
	//printf("about to release kernelLockLock in acquire lock %i syscall!\n", lockIndex);
	//kernelLockLock->Release();
	//printf("released kernelLockLock in acquire lock %i syscall!\n", lockIndex);
	//return errorCode;
}

int ReleaseLock_Syscall(int vaddr, int length){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of lock; 3 = lock does not exist */
	
	int errorCode = 0;
	#ifdef NETWORK
	char* lockName;
	if ( !(lockName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[length] = '\0';
	
		if ( copyin(vaddr,length,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	int bogusVal = 0;
	char msg[MaxMailSize];
	int needReply = 1;
	sprintf(msg,"%i %s %i", SC_ReleaseLock, lockName, needReply);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 0;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in destroy lock syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int lockIndex = vaddr; //TEMP TO FIX COMPILE, BUT SHOULD TECHNICALLY WORK
	//printf("about to acquire kernelLockLock in release lock %i syscall!\n", lockIndex);
	kernelLockLock->Acquire();
	//printf("acquired kernelLockLock in release lock %i syscall!\n", lockIndex);
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for index %i for Release Lock Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex] == NULL){
		printf("ERROR: Lock at index %i does not exist for Release Lock Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Release Lock Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		return errorCode;
	}
	
	/*if(kernelLocks[lockIndex]->inUse != 0){
		printf("ERROR: Lock at index %i trying to release but not acquired first!\n", lockIndex);
		errorCode = 4;
		kernelLockLock->Release();
		return errorCode;
	}*/
	
	//printf("Lock at index %i successfully about to be released!\n", lockIndex);
	kernelLocks[lockIndex]->lock->Release();
	//printf("Lock at index %i successfully released!\n", lockIndex);
	
	if(kernelLocks[lockIndex]->inUse == 0 && kernelLocks[lockIndex]->toBeDeleted == 1){ // no more threads waiting to acquire
		kernelLocks[lockIndex]->lock = NULL; // or use delete?? 
		kernelLocks[lockIndex]->addrSpace = NULL; // or use delete?? 
					
		printf("Lock at index %i successfully deleted!\n", lockIndex);
	}
	
	kernelLockLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelLocks[lockIndex] != NULL){
			if(kernelLocks[lockIndex]->addrSpace == currentThread->space){
				if(kernelLocks[lockIndex]->inUse > 0){ // if someone has acquired the lock, then you should release it, otherwise shouldn't attempt a release
					printf("Lock at index %i successfully about to be released!\n", lockIndex);
					kernelLocks[lockIndex]->lock->Release();
					kernelLocks[lockIndex]->inUse--;
					printf("Lock at index %i successfully released!\n", lockIndex);
				} else {
					printf("ERROR: Lock at index %i trying to release but not acquired first!\n", lockIndex);
				}
				
				if(kernelLocks[lockIndex]->inUse == 0 && kernelLocks[lockIndex]->toBeDeleted == 1){ // no more threads waiting to acquire
					kernelLocks[lockIndex]->lock = NULL; // or use delete?? 
					kernelLocks[lockIndex]->addrSpace = NULL; // or use delete?? 
					
					printf("Lock at index %i successfully deleted!\n", lockIndex);
				}
			} else {
				printf("ERROR: Lock at index %i not owned by process for Release Lock Syscall!\n", lockIndex);
				errorCode = 2;
			}
		} else {
			printf("ERROR: Lock at index %i does not exist for Release Lock Syscall!\n", lockIndex);
			errorCode = 3;
		}
	}*/
	
	//printf("about to release kernelLockLock in release lock %i syscall!\n", lockIndex);
	//kernelLockLock->Release();
	//printf("released kernelLockLock in release lock %i syscall!\n", lockIndex);
	//return errorCode;
}

/* Condition Variable */
int CreateCondition_Syscall(int vaddr, int length){
	
	#ifdef NETWORK
	char* cvName;
	if ( !(cvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		cvName[length] = '\0';
	
		if ( copyin(vaddr,length,cvName) == -1 ) {
			printf("error in copy in\n");
			delete[] cvName;
			return -1;
		}
	}
	
	
	
	//char* cvName = new char[20];
	//sprintf(cvName, "m%is%icv%i", currentThread->getMachineId(), currentThread->spaceId, nextConditionIndex);
	//printf("cvName = %s\n", cvName);
	
	int bogusVal = 0;
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s",SC_Condition_Create, cvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 1;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in create condition syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	int conditionId = atoi(receivedMessage); //parse message
	return conditionId;
	#else
	kernelConditionLock->Acquire();
	int createdConditionIndex = nextConditionIndex;
	if(createdConditionIndex < MAX_CONDITIONS - 1){ /* if the condition index is within the bounds of the arrray */
		char* conditionName = new char[20];
		sprintf(conditionName, "Cond %i", createdConditionIndex);
		
		KernelCondition* createdKernelCondition = new KernelCondition(new Condition(conditionName), currentThread->space);
		
		kernelConditions[createdConditionIndex] = createdKernelCondition;
		
		nextConditionIndex++; /* increment the condition index for the next condition */
		kernelConditionLock->Release();
		return createdConditionIndex;
	} else { /* it's not within the bounds of the array */
		printf("ERROR: Array Index Out of Bounds for Create Condition Syscall!\n");
		createdConditionIndex = -1;
		kernelConditionLock->Release();
		return createdConditionIndex;
	}
	#endif
	//kernelConditionLock->Release();
	//return createdConditionIndex;
}

int DestroyCondition_Syscall(int vaddr, int length){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of condition; 3 = condition does not exist */
	
	int errorCode = 0;
	#ifdef NETWORK
	char* cvName;
	if ( !(cvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		cvName[length] = '\0';
	
		if ( copyin(vaddr,length,cvName) == -1 ) {
			printf("error in copy in\n");
			delete[] cvName;
			return -1;
		}
	}
	
	int bogusVal = 0;	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s",SC_Condition_Destroy, cvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 1;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in destroy condition syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int conditionIndex = vaddr;
	kernelConditionLock->Acquire();
	
	if(conditionIndex < 0 || conditionIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for index %i for Destroy Condition Syscall!\n", conditionIndex);
		errorCode = 1;
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex] == NULL){
		printf("ERROR: Condition at index %i does not exist for Destroy Condition Syscall!\n", conditionIndex);
		errorCode = 3;
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
		printf("ERROR: Condition at index %i not owned by process for Destroy Condition Syscall!\n", conditionIndex);
		errorCode = 2;
		kernelConditionLock->Release();
		return errorCode;
	}
	
	kernelConditions[conditionIndex]->toBeDeleted = 1;
	//printf("Condition at index %i soon to be destroyed!\n", conditionIndex);
	kernelConditionLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelConditions[conditionIndex] != NULL){
			if(kernelConditions[conditionIndex]->addrSpace == currentThread->space){
				kernelConditions[conditionIndex]->toBeDeleted = 1;
				//printf("Condition at index %i soon to be destroyed!\n", conditionIndex);
			} else {
				printf("ERROR: Condition at index %i not owned by process for Destroy Condition Syscall!\n", conditionIndex);
				errorCode = 2;
			}
		} else {
			printf("ERROR: Condition at index %i does not exist for Destroy Condition Syscall!\n", conditionIndex);
			errorCode = 3;
		} 
	}*/
	
	//kernelConditionLock->Release();
	//return errorCode;
}

int ConditionWait_Syscall(int cVaddr, int clength, int lVaddr, int lLength){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of condition; 3 = condition does not exist */
	int errorCode = 0;
	#ifdef NETWORK
	char* cvName;
	if ( !(cvName = new char[clength + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		cvName[clength] = '\0';
	
		if ( copyin(cVaddr,clength,cvName) == -1 ) {
			printf("error in copy in\n");
			delete[] cvName;
			return -1;
		}
	}
	
	char* lockName;
	if ( !(lockName = new char[lLength + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[lLength] = '\0';
	
		if ( copyin(lVaddr,lLength,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s %s",SC_Condition_Wait, lockName, cvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 1;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in condition wait syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int conditionIndex = cVaddr;
	int lockIndex = clength;
	kernelConditionLock->Acquire();
	kernelLockLock->Acquire();
	
	if(conditionIndex < 0 || conditionIndex > MAX_CONDITIONS - 1){
		printf("ERROR: Array Index Out of Bounds for condition index %i for Condition Wait Syscall!\n", conditionIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for lock index %i for Condition Wait Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex] == NULL){
		printf("ERROR: Condition at index %i does not exist for Condition Wait Syscall!\n", conditionIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex] == NULL){
		printf("ERROR: Lock at index %i does not exist for Condition Wait Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
		printf("ERROR: Condition at index %i not owned by process for Condition Wait Syscall!\n", conditionIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Condition Wait Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	kernelConditions[conditionIndex]->inUse++; // need to change somehow?
	kernelLockLock->Release();
	kernelConditionLock->Release();
				
	//printf("Condition at index %i with lock at index %i successfully about to wait! inUse = %i\n", conditionIndex, lockIndex, kernelConditions[conditionIndex]->inUse);
	kernelConditions[conditionIndex]->condition->Wait(kernelLocks[lockIndex]->lock);
	kernelConditionLock->Acquire(); // to be released later
				
	kernelConditions[conditionIndex]->inUse--;
	//printf("Condition at index %i with lock at index %i successfully waited and now awake! inUse = %i\n", conditionIndex, lockIndex, kernelConditions[conditionIndex]->inUse);
	
	kernelConditionLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelConditions[conditionIndex] != NULL && kernelLocks[lockIndex] != NULL){
			if(kernelConditions[conditionIndex]->addrSpace == currentThread->space && kernelLocks[lockIndex]->addrSpace == currentThread->space){
				kernelConditions[conditionIndex]->inUse--; // need to change somehow?			
				kernelConditionLock->Release();
				
				printf("Condition at index %i with lock at index %i successfully about to wait!\n", conditionIndex, lockIndex);
				kernelConditions[conditionIndex]->condition->Wait(kernelLocks[lockIndex]->lock);
				kernelConditionLock->Acquire(); // to be released later
				
				kernelConditions[conditionIndex]->inUse++;
				printf("Condition at index %i with lock at index %i successfully waited!\n", conditionIndex, lockIndex);
			} else if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
				printf("ERROR: Condition at index %i not owned by process for Condition Wait Syscall!\n", conditionIndex);
				errorCode = 2;
			} else if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
				printf("ERROR: Lock at index %i not owned by process for Condition Wait Syscall!\n", lockIndex);
				errorCode = 2;
			}
		} else if(kernelConditions[conditionIndex] == NULL){
			printf("ERROR: Condition at index %i does not exist for Condition Wait Syscall!\n", conditionIndex);
			errorCode = 3;
		} else if(kernelLocks[lockIndex] == NULL){
			printf("ERROR: Lock at index %i does not exist for Condition Wait Syscall!\n", lockIndex);
			errorCode = 3;
		}
	}*/
	
	//kernelConditionLock->Release();
	//return errorCode;
}

int ConditionSignal_Syscall(int cVaddr, int cLength, int lVaddr, int lLength){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of condition; 3 = condition does not exist */
	int errorCode = 0;
	#ifdef NETWORK
	char* cvName;
	if ( !(cvName = new char[cLength]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		cvName[cLength] = '\0';
	
		if ( copyin(cVaddr,cLength,cvName) == -1 ) {
			printf("error in copy in\n");
			delete[] cvName;
			return -1;
		}
	}
	
	char* lockName;
	if ( !(lockName = new char[lLength]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[lLength] = '\0';
	
		if ( copyin(lVaddr,lLength,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s %s",SC_Condition_Signal, lockName, cvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 1;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	//printf("sending signal for %s with %s\n",cvName, lockName);
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	//printf("sent signal for %s with %s\n",cvName, lockName);
	if(!success){
		printf("postOffice send failed in condition signal syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	//printf("waiting to receive for %s with %s\n", cvName, lockName);
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	//printf("received %s for %s\n", cvName, lockName);
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else
	int conditionIndex = cVaddr;
	int lockIndex = cLength;
	kernelConditionLock->Acquire();
	kernelLockLock->Acquire();
	
	if(conditionIndex < 0 || conditionIndex > MAX_CONDITIONS - 1){
		printf("ERROR: Array Index Out of Bounds for condition index %i for Condition Signal Syscall!\n", conditionIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for lock index %i for Condition Signal Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex] == NULL){
		printf("ERROR: Condition at index %i does not exist for Condition Signal Syscall!\n", conditionIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex] == NULL){
		printf("ERROR: Lock at index %i does not exist for Condition Signal Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
		printf("ERROR: Condition at index %i not owned by process for Condition Signal Syscall!\n", conditionIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Condition Signal Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->condition->isEmpty() == 1){
		printf("ERROR: Condition at index %i with lock %i has no threads in waitQueue in signal syscall!\n", conditionIndex, lockIndex);
		errorCode = 4;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	kernelConditions[conditionIndex]->condition->Signal(kernelLocks[lockIndex]->lock);
	//printf("Condition at index %i with lock at index %i successfully signalled!\n", conditionIndex, lockIndex);
	kernelLockLock->Release();
	kernelConditionLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelConditions[conditionIndex] != NULL && kernelLocks[lockIndex] != NULL){
			if(kernelConditions[conditionIndex]->addrSpace == currentThread->space && kernelLocks[lockIndex]->addrSpace == currentThread->space){
				if(kernelConditions[conditionIndex]->condition->isEmpty() == 0){ // waitQueue is not empty
					kernelConditions[conditionIndex]->condition->Signal(kernelLocks[lockIndex]->lock);
					printf("Condition at index %i with lock at index %i successfully signalled!\n", conditionIndex, lockIndex);
				} else {
					printf("ERROR: Condition at index %i has no threads in waitQueue!\n", conditionIndex);
				}
			} else if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
				printf("ERROR: Condition at index %i not owned by process for Condition Signal Syscall!\n", conditionIndex);
				errorCode = 2;
			} else if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
				printf("ERROR: Lock at index %i not owned by process for Condition Signal Syscall!\n", lockIndex);
				errorCode = 2;
			}
		} else if(kernelConditions[conditionIndex] == NULL){
			printf("ERROR: Condition at index %i does not exist for Condition Signal Syscall!\n", conditionIndex);
			errorCode = 3;
		} else if(kernelLocks[lockIndex] == NULL){
			printf("ERROR: Lock at index %i does not exist for Condition Signal Syscall!\n", lockIndex);
			errorCode = 3;
		}
	}*/
	
	//kernelConditionLock->Release();
	//return errorCode;
}

int ConditionBroadcast_Syscall(int cVaddr, int cLength, int lVaddr, int lLength){ /* return an error code: 0 = success; 1 = index out of bounds; 2 = process isn't owner of condition; 3 = condition does not exist */
	int errorCode = 0;
	#ifdef NETWORK
	char* cvName;
	if ( !(cvName = new char[cLength + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		cvName[cLength] = '\0';
	
		if ( copyin(cVaddr,cLength,cvName) == -1 ) {
			printf("error in copy in\n");
			delete[] cvName;
			return -1;
		}
	}
	
	char* lockName;
	if ( !(lockName = new char[lLength]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		lockName[lLength] = '\0';
	
		if ( copyin(lVaddr,lLength,lockName) == -1 ) {
			printf("error in copy in\n");
			delete[] lockName;
			return -1;
		}
	}
	
	char msg[MaxMailSize];
	sprintf(msg,"%i %s %s",SC_Condition_Broadcast, lockName, cvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	//outPktHdr.to = 1;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in condition broadcast syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
	#else	
	int conditionIndex = cVaddr;
	int lockIndex = cLength;
	kernelConditionLock->Acquire();
	kernelLockLock->Acquire();
	
	if(conditionIndex < 0 || conditionIndex > MAX_CONDITIONS - 1){
		printf("ERROR: Array Index Out of Bounds for condition index %i for Condition Broadcast Syscall!\n", conditionIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(lockIndex < 0 || lockIndex > MAX_LOCKS - 1){
		printf("ERROR: Array Index Out of Bounds for lock index %i for Condition Broadcast Syscall!\n", lockIndex);
		errorCode = 1;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex] == NULL){
		printf("ERROR: Condition at index %i does not exist for Condition Broadcast Syscall!\n", conditionIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex] == NULL){
		printf("ERROR: Lock at index %i does not exist for Condition Broadcast Syscall!\n", lockIndex);
		errorCode = 3;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
		printf("ERROR: Condition at index %i not owned by process for Condition Broadcast Syscall!\n", conditionIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
		printf("ERROR: Lock at index %i not owned by process for Condition Broadcast Syscall!\n", lockIndex);
		errorCode = 2;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	if(kernelConditions[conditionIndex]->condition->isEmpty() == 1){
		//printf("ERROR: Condition at index %i with lock %i has no threads in waitQueue in broadcast syscall!\n", conditionIndex, lockIndex);
		errorCode = 4;
		kernelLockLock->Release();
		kernelConditionLock->Release();
		return errorCode;
	}
	
	//printf("Condition at index %i with lock at index %i successfully about to broadcast!\n", conditionIndex, lockIndex);
	kernelConditions[conditionIndex]->condition->Broadcast(kernelLocks[lockIndex]->lock);
	//printf("Condition at index %i with lock at index %i successfully broadcasted!\n", conditionIndex, lockIndex);
	
	kernelLockLock->Release();
	kernelConditionLock->Release();
	return errorCode;
	#endif
	/*if(errorCode != 1){
		if(kernelConditions[conditionIndex] != NULL && kernelLocks[lockIndex] != NULL){
			if(kernelConditions[conditionIndex]->addrSpace == currentThread->space && kernelLocks[lockIndex]->addrSpace == currentThread->space){
				printf("Condition at index %i with lock at index %i successfully about to broadcast!\n", conditionIndex, lockIndex);
				kernelConditions[conditionIndex]->condition->Broadcast(kernelLocks[lockIndex]->lock);
				printf("Condition at index %i with lock at index %i successfully broadcasted!\n", conditionIndex, lockIndex);
			} else if(kernelConditions[conditionIndex]->addrSpace != currentThread->space){
				printf("ERROR: Condition at index %i not owned by process for Condition Broadcast Syscall!\n", conditionIndex);
				errorCode = 2;
			} else if(kernelLocks[lockIndex]->addrSpace != currentThread->space){
				printf("ERROR: Lock at index %i not owned by process for Condition Broadcast Syscall!\n", lockIndex);
				errorCode = 2;
			}
		} else if(kernelConditions[conditionIndex] == NULL){
			printf("ERROR: Condition at index %i does not exist for Condition Brodcast Syscall!\n", conditionIndex);
			errorCode = 3;
		} else if(kernelLocks[lockIndex] == NULL){
			printf("ERROR: Lock at index %i does not exist for Condition Broadcast Syscall!\n", lockIndex);
			errorCode = 3;
		}
	}*/
	
	//kernelConditionLock->Release();
	//return errorCode;
}

#ifdef NETWORK
/* monitor variable stuff */

int CreateMonitor_Syscall(int vaddr, int length){
	char* mvName;
	if ( !(mvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		mvName[length] = '\0';
	
		if ( copyin(vaddr,length,mvName) == -1 ) {
			printf("error in copy in\n");
			delete[] mvName;
			return -1;
		}
	}
	
	

	//char* mvName = new char[20];
	//sprintf(mvName, "m%is%imv%i", currentThread->getMachineId(), currentThread->spaceId, nextMonitorIndex);
	//printf("mvName = %s\n", mvName);
	nextMonitorIndex++;
	
	int bogusVal = 0;

	char msg[MaxMailSize];
	sprintf(msg,"%i %s",SC_CreateMonitor, mvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in create monitor syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	int conditionId = atoi(receivedMessage); //parse message
	return conditionId;
}

int DestroyMonitor_Syscall(int vaddr, int length, int monitorIndex){
	char* mvName;
	if ( !(mvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		mvName[length] = '\0';
	
		if ( copyin(vaddr,length,mvName) == -1 ) {
			printf("error in copy in\n");
			delete[] mvName;
			return -1;
		}
	}

	//char* mvName = new char[20];
	//sprintf(mvName, "m%is%imv%i", currentThread->getMachineId(), currentThread->spaceId, nextMonitorIndex);
	//printf("mvName = %s\n", mvName);
	nextMonitorIndex++;
	
	int bogusVal = 0;
	
	int errorCode = 0;
	char msg[MaxMailSize];
	sprintf(msg,"%i %s", SC_DestroyMonitor, mvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in destroy monitor syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
}

int SetMonitor_Syscall(int vaddr, int length, int val, int pos){
	char* mvName;
	if ( !(mvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		mvName[length] = '\0';
	
		if ( copyin(vaddr,length,mvName) == -1 ) {
			printf("error in copy in\n");
			delete[] mvName;
			return -1;
		}
	}

	//char* mvName = new char[20];
	//sprintf(mvName, "m%is%imv%i", currentThread->getMachineId(), currentThread->spaceId, nextMonitorIndex);
	//printf("mvName = %s\n", mvName);
	nextMonitorIndex++;
	
	int bogusVal = 0;
	
	int errorCode = 0;
	char msg[MaxMailSize];
	sprintf(msg,"%i %i %i %s", SC_SetMonitor, val, pos, mvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in set monitor syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
}

int GetMonitor_Syscall(int vaddr, int length, int pos){
	char* mvName;
	if ( !(mvName = new char[length + 1]) ) {
		printf("Error allocating kernel buffer for write!\n");
		return -1;
	} else {
	
		mvName[length] = '\0';
	
		if ( copyin(vaddr,length,mvName) == -1 ) {
			printf("error in copy in\n");
			delete[] mvName;
			return -1;
		}
	}

	//char* mvName = new char[20];
	//sprintf(mvName, "m%is%imv%i", currentThread->getMachineId(), currentThread->spaceId, nextMonitorIndex);
	//printf("mvName = %s\n", mvName);
	nextMonitorIndex++;
	
	int bogusVal = 0;
	
	int errorCode = 0;
	char msg[MaxMailSize];
	sprintf(msg,"%i %i %s", SC_GetMonitor, pos, mvName);
	
	//set header information
	outPktHdr.to = rand()%NUM_SERVERS;
	outPktHdr.from = currentThread->getMachineId();
	outMailHdr.to = 0;
	//outMailHdr.from = currentThread->getThreadId(); /** need to set this correctly to threadId for later projects*/
	outMailHdr.from = currentThread->spaceId;
	outMailHdr.length = strlen(msg) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, msg); //send message
	if(!success){
		printf("postOffice send failed in get monitor syscall\n");
		interrupt->Halt();
	}
	
	//postOffice->Receive(currentThread->getThreadId(), &inPktHdr, &inMailHdr, receivedMessage); //receive message
	postOffice->Receive(currentThread->spaceId, &inPktHdr, &inMailHdr, receivedMessage); //receive message
	fflush(stdout);
	
	errorCode = atoi(receivedMessage);
	return errorCode;
}

#endif

void Printing_Syscall(unsigned int vaddr, int id, int first, int second) {

	/* Modeled after Write_Syscall
	
	Conditions that must be met:
	-all lines must end in either '\0' or '\n', also of now no error will be called if this isn't handled
	-all inputs must contain exactly two integers, even if you don't want to print integers (only the first two will print)
	-you can only use %i twice (sort of goes with the previous line) or error will be thrown
	-maximum of 200 characters per printline (is flexible if we need to change it), won't throw an error with more than 200

	*/
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
	int len = 200;
	
	/*Boolean type variables that indicate when to stop printing because all good chars have been printed*/
	int printFirst = 0;
	int printSecond = 0;

    if ( id == ConsoleInput) return;
    
		if ( !(buf = new char[len]) ) {
			printf("%s","Error allocating kernel buffer for write!\n");
			return;
		} else {
			if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
    }

    if ( id == ConsoleOutput) {
		
		for (int ii=0; ii < len; ii++) {
		
			/*If an escape character, set a flag and don't print*/
			if(buf[ii] == '\n'){
				printf("\n");				
				return;
			}
			else if(buf[ii] == '\0'){				
				return;
			}
			else if(buf[ii] == '%' && !printFirst){
				//printing the first integer
				printFirst = 1;
			}
			else if(buf[ii] == '%' && !printSecond){
				//printing the second interger
				printSecond = 1;
			}
			else if(buf[ii] == '%' && printFirst > 1&& printSecond > 1){
				//printerror if there are more than 3 integers to be printere
				printf("\n***Error: Can not handle more than three integers.***\n");
			}
			else if(printFirst == 1){
			
				/*If next char after \ is an n, then endline, else just stop printing*/
				if(buf[ii] == 'i'){
					printf("%i", first);
				} 
				else{
					printf("\n***Error: Can only handle integers.***\n");
				}
				
				//can not print the first again, but also not reset
				printFirst = 2;
			}
			else if(printSecond == 1){
			
				/*If next char after \ is an n, then endline, else just stop printing*/
				if(buf[ii] == 'i'){
					printf("%i",second);
				}
				else{
					printf("\n***Error: Can only handle integers.***\n");
				}
				 
				//can not print the second again, but also not reset
				printSecond = 2;
			}
			else{
				printf("%c",buf[ii]);
			}
		
			
		}		

    } else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			f->Write(buf, len);
		} else {
			printf("%s","Bad OpenFileId passed to Write\n");
			len = -1;
		}
    }

    delete[] buf;
}

int  Random_Syscall(){
	int randnum;
	randnum = (int)rand();

	return randnum;
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
	
    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
			      
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
		case SC_Yield:
		DEBUG('a', "Yield syscall.\n");
		Yield_Syscall();
		break;
		case SC_CreateLock:
		DEBUG('a', "Create Lock syscall.\n");
		rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_DestroyLock:
		DEBUG('a', "Destroy Lock syscall.\n");
		DestroyLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_AcquireLock:
		DEBUG('a', "Acquire Lock syscall.\n");
		AcquireLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_ReleaseLock:
		DEBUG('a', "Release Lock syscall.\n");
		ReleaseLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Condition_Create:
		DEBUG('a', "Create a Condition Variable.\n");	
		rv = CreateCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Condition_Destroy:
		DEBUG('a', "Destroy a Condition Variable.\n");
		DestroyCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Condition_Wait:
		DEBUG('a', "Condition Variable Waiting.\n");
		ConditionWait_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
		break;
		case SC_Condition_Signal:
		DEBUG('a', "Condition Variable Signaling.\n");
		ConditionSignal_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
		break;
		case SC_Condition_Broadcast:
		DEBUG('a', "Condition Variable Broadcasting.\n");
		ConditionBroadcast_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
		break;
		case SC_Exec:
		DEBUG('a', "Execute Thread.\n");
		Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Fork:
		DEBUG('a', "Fork Thread.\n");
		Fork_Syscall(machine->ReadRegister(4));
		break;
		case SC_Exit:
		DEBUG('a', "Exit Thread.\n");
		Exit_Syscall(machine->ReadRegister(4));
		//currentThread->Finish();
		break;
		case SC_Printing:
		DEBUG('a', "Writing to console.\n");
		Printing_Syscall(machine->ReadRegister(4),machine->ReadRegister(5),machine->ReadRegister(6),machine->ReadRegister(7));
		break;
		case SC_Random:
		DEBUG('a', "Generating Random Number.\n");
		rv = Random_Syscall();
		break;
		#ifdef NETWORK
		case SC_CreateMonitor:
		rv = CreateMonitor_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_DestroyMonitor:
		rv = DestroyMonitor_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		case SC_SetMonitor:
		rv = SetMonitor_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
		break;
		case SC_GetMonitor:
		rv = GetMonitor_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
		break;
		#endif
	}
	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	//printf("machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));\n");
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	//printf("machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));\n");
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	//printf("machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);\n");
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } else if(which == PageFaultException){
		/**increment number of page faults?*/
		#ifdef USE_TLB
		HandlePageFault();
		#endif
	} else {
	  printf("request made by %i\n", currentThread->spaceId);
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}

